package com.atguigu.gmall.gateway.filter;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.gateway.constant.RedisConst;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;

/**
 * @author 李旭
 * @date 2022/1/17 9:26
 * @Description:
 *
 *   自定义的全局过滤器
 *   1、校验是否登录
 *   2、是否权限
 *
 *    //当前过滤器的执行顺序
 *
 *    1、网关：默认情况下 有9大全局过滤器
 *    2、网关：自定义全局过滤器
 *    共 10个全局过滤器
 *
 *     全局过滤器执行顺序
 *     负整数最大               ~ -1 ~ 0 ~ 1 ~    正整数最大
 *     -Integer.MAX_VALUE                         Integer.MAX_VALUE
 *     从左至右执行优先级越来越低
 *
 *
 *
 *     原来： Tomcat服务器对象的Servlet容器   HttpServletRequest HttpServletResponse   非响应式 传统
 *     现在： webFlux服务器 使用gateway      ServerHttpRequest  ServerHttpResponse     响应式  新的
 *
 *     Redis ： 非响应式    响应式
 *
 *
 *
 */
@Component
public class GlobalLoginFilter implements GlobalFilter, Ordered {


    public static final String TOKEN = "token";

    public static final String USERTEMPID = "userTempId";

    @Autowired
    private RedisTemplate redisTemplate;

    public static final String LOGINURL = "http://passport.gmall.com/login.html?originUrl=";

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    //配置文件
    @Value("${url.path}")
    private String[] urlPaths;


    //过滤器方法
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        //1:获取Request对象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        ///api/product/inner/....
        String path = request.getURI().getPath();
        //http://item.gmall.com/api/product/inner/....
        System.out.println(request.getURI().toString());
        //2:获取用户ID （可能请求头 、可能是Cookie）
        String userId = getUserId(request);
        //3: 校验是否为内部资源    内部资源 ：网关不可以路由
        if(antPathMatcher.match("/**/inner/**",path)){
            //内部资源
            return out(response,ResultCodeEnum.PERMISSION);
        }
        //4:校验当前请求的URL地址 是否是需要登录的  异步请求
        //  //http://api.gmall.com/api/product/auth/....
        if(antPathMatcher.match("/**/auth/**",path) && StringUtils.isEmpty(userId)){
            // 请求要求是必须登录的 、但是用户未登录
            return out(response,ResultCodeEnum.LOGIN_AUTH);
        }
        //5:校验当前请求的URL地址 是否是需要登录的 同步请求
        //http://cart.gmall.com/cart.html
        //http://order.gmall.com/order.html
        //http://order.gmall.com/trade.html
        //http://pay.gmall.com/pay.html
        if(null != urlPaths && urlPaths.length > 0){
            for (String urlPath : urlPaths) {
               // if(antPathMatcher.match(urlPath,path) && StringUtils.isEmpty(userId)){
                //   /pay.html    pay.html
                if(path.indexOf(urlPath) != -1 && StringUtils.isEmpty(userId)){
                    //当前路径是必须登录 但是用户未登录
                    //重定向到登录页面
                    //1:响应行 （状态码） 303
                    response.setStatusCode(HttpStatus.SEE_OTHER);
                    try {
                        //2:响应头 设置重定向的地址
                        response.getHeaders().add(HttpHeaders.LOCATION,
                                LOGINURL +
                                        URLEncoder.encode(request.getURI().toString(),"UTF-8"));
                        //3:响应
                        return response.setComplete();
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                }
            }

        }
        //6:如果用户登录了 获取用户的真实ID  Request对象中Cookie带过来了
        if(!StringUtils.isEmpty(userId)){
            request.mutate().header("userId",userId);
        }
        //7:如果用户未登录  获取临时用户ID  Request对象中Cookie带过来了
        String userTempId = getUserTempId(request);
        if(!StringUtils.isEmpty(userTempId)){
            request.mutate().header("userTempId",userTempId);
        }
        //放行
        return chain.filter(exchange);
    }

    //统一返回
    private Mono<Void> out(ServerHttpResponse response,ResultCodeEnum resultCodeEnum) {
        Result<Object> result = Result.build(null, resultCodeEnum);
        String r = JSONObject.toJSONString(result);
        DataBufferFactory dataBufferFactory = response.bufferFactory();
        DataBuffer dataBuffer = dataBufferFactory.wrap(r.getBytes());
        response.getHeaders().add(HttpHeaders.CONTENT_TYPE,
                MediaType.APPLICATION_JSON_UTF8_VALUE);
        return response.writeWith(Mono.just(dataBuffer));

    }

    //从Request对象中Cookie中获取出临时用户的ID
    public String getUserTempId(ServerHttpRequest request){

        String userTempId = request.getHeaders().getFirst(USERTEMPID);
        if(StringUtils.isEmpty(userTempId)){
            HttpCookie httpCookie = request.getCookies().getFirst(USERTEMPID);
            if(null != httpCookie){
                userTempId = httpCookie.getValue();
            }
        }
        return userTempId;
    }


    //通过令牌真实用户的ID （Ajax可能请求头 、 同步可能是Cookie）
    public String getUserId(ServerHttpRequest request){
        //1：从请求头获取令牌
        String token = request.getHeaders().getFirst(TOKEN);
        if(StringUtils.isEmpty(token)){
            HttpCookie httpCookie = request.getCookies().getFirst(TOKEN);
            if(null != httpCookie){
                token = httpCookie.getValue();
            }
        }
        if(!StringUtils.isEmpty(token)){
            //浏览器上有令牌
            //2:查询缓存进行令牌校验
            return (String) redisTemplate.opsForValue().get(RedisConst.USER_LOGIN_KEY_PREFIX + token);

        }
        //用户未登录
        return null;
    }



    //当前过滤器的执行顺序
    @Override
    public int getOrder() {
        return 0;
    }
}
